package com.runa.protocol.dto;

import com.runa.protocol.enums.ModbusFunctionCode;
import com.runa.protocol.enums.ModbusType;
import com.runa.protocol.util.ByteUtil;

import java.util.List;

public class ModbusWriteRequestData extends ModbusRequestData {

    private int writeCount;

    private int byteCount;

    public ModbusWriteRequestData(int slaveId, ModbusFunctionCode functionCode,
                                  int address, List<ModbusPoint> points, ModbusType type) {
        super(slaveId, functionCode, address, type);
        if (ModbusFunctionCode.WRITE_COILS.equals(functionCode)) {
            coilsData(points);
        } else {
            registerData(points);
        }
    }

    public ModbusWriteRequestData(int slaveId, ModbusFunctionCode functionCode,
                                  int address, ModbusType type, byte[] bytes) {
        super(slaveId, functionCode, address, type);
        if (ModbusFunctionCode.WRITE_REGISTERS.equals(functionCode)) {
            registerData(bytes);
        }
    }

    /**
     * 构造线圈数据
     * @param points
     * @return
     */
    private void coilsData(List<ModbusPoint> points) {
        writeCount = points.get(points.size()-1)
                .getAddress() - points.get(0).getAddress() + 1;
        byteCount = (writeCount+7) >> 3; // ÷8
        data = new byte[3 + byteCount];
        data[0] = (byte) (writeCount >> 8);
        data[1] = (byte) (writeCount & 0xff);
        data[2] = (byte) byteCount;
        int b = 0;
        int index = 3;
        int offset = 0;
        for (int i=0; i<points.size(); i++) {
            ModbusPoint point = points.get(i);
            offset = i%8;
            if ((Boolean)point.getValue()) {
                b |= (1 << offset);
            }
            if (offset == 7) {
                data[index++] = (byte) b;
                b = 0;
            }
        }
        if (offset != 7) {
            data[index] = (byte) b;
        }
    }

    private void registerData(byte[] bytes) {
        writeCount = bytes.length / 2;
        byteCount = bytes.length;
        data = new byte[3 + byteCount];
        data[0] = (byte) (writeCount >> 8);
        data[1] = (byte) (writeCount & 0xff);
        data[2] = (byte) byteCount;
        System.arraycopy(bytes, 0, data, 3, byteCount);
    }

    /**
     * 构造寄存器数据（布尔型点位不可以写值，不实现）
     * @param points
     * @return
     */
    private void registerData(List<ModbusPoint> points) {
        int last = points.size() - 1;
        writeCount = points.get(last).getAddress() - points.get(0)
                .getAddress() + points.get(last).getRegisterCount();
        byteCount = writeCount << 1; // *2
        data = new byte[3 + byteCount];
        data[0] = (byte) (writeCount >> 8);
        data[1] = (byte) (writeCount & 0xff);
        data[2] = (byte) byteCount;
        int index = 3;
        /*int b = 0;
        IModbusPoint prePoint = null;*/
        for (ModbusPoint point : points) {
            /*if(prePoint != null &&
                    !prePoint.getModbusAddress().equals(point.getModbusAddress())) {
                ByteUtil.shortToByte((short) b, data, index, prePoint.getByteOrder());
                b = 0;
                index += 2;
                prePoint = null;
            }*/
            switch (point.getDataType()) {
                /*case Modbus_Boolean:
                    //逻辑还要验证!!
                    if (prePoint != null) {
                        if (prePoint.getModbusAddress().equals(point.getModbusAddress())){
                            if ((Boolean) point.getValue())
                                b |= (1 << point.getDeviation());
                        }
                    } else {
                        if ((Boolean) point.getValue())
                            b = (1 << point.getDeviation());
                    }
                    prePoint = point;
                    if (point == points.get(last))
                        ByteUtil.shortToByte((short) b, data, index, prePoint.getByteOrder());
                    break;*/
                case Modbus_Int16:
                case Modbus_UInt16:
                    ByteUtil.shortToByte((Short)point.getValue(), data, index, point.getByteOrder());
                    index += 2;
                    break;
                case Modbus_Int32:
                case Modbus_UInt32:
                    ByteUtil.intToByte((Integer)point.getValue(), data, index, point.getByteOrder());
                    index += 4;
                    break;
                case Modbus_Float:
                    ByteUtil.floatToByte((Float)point.getValue(), data, index, point.getByteOrder());
                    index += 4;
                case Modbus_Double:
                    //TODO
                    break;
                default:
            }
        }
    }

    @Override
    public byte[] toByteArray() {
        return data;
    }

    @Override
    public boolean isReadWrite() {
        return false;
    }

    @Override
    public boolean check(IResponseData interData) {
        try {
            ModbusResponseData responseData = (ModbusResponseData) interData;
            if (this.getTransSign() != responseData.getTransSign()) return false;
            if (responseData.getType().equals(getType()) &&
                    responseData.getSlaveId() == getSlaveId() &&
                    responseData.getFunctionCode().equals(getFunctionCode())) {
                byte[] data = responseData.getData();
                int address = (data[0]<<8&0xff00) + (data[1]&0xff);
                int count = (data[2]<<8&0xff00) + (data[3]&0xff);
                return address == getAddress() && count == writeCount;
            }
        } catch (Exception ignored) {}
        return false;
    }

}
